//File: E_Resource.cpp
#include "E_Resource.h"
#include <fstream>
#include <sstream>
#include "Shader.h"
#include "Utility.h"

using namespace std;

// |------- class Resource -------|
std::list<Prop_Static>	Resource::DrawList;
map<std::string,Model> Resource::Models;
map<std::string,GLuint> Resource::Textures;
map<std::string,GLuint> Resource::Shaders;
map<std::string,GLuint> Resource::Shader_Programs;
Node					Resource::Config;


Resource::Resource(void)
{
}
Resource::~Resource(void)
{

}


Model* Resource::LoadModel(string Path)
{
	auto Find = Models.find(Path);

	if (Find == Models.end())
	{
		Utility::Log("Resource: Allocating new [Model] resource: '"+Path+"'");

		Models.insert(Models.end(),pair<string,Model>(Path,Collada::LoadModel(Path)));
		return LoadModel(Path);
	}
	else
	{
		return &Find->second;
	}
}
GLuint Resource::LoadShader(std::string Path, GLenum ShaderType)
{
	auto Find = Shaders.find(Path);
	if (Find == Shaders.end())
	{
		Utility::Log("Resource: Allocating new [Shader] resource: '"+Path+"'");

		GLuint	Shader		=	Shader::LoadShader(Path,ShaderType);
		pair<string,GLuint>		Pair(Path,Shader);
		Shaders.insert(Shaders.end(),Pair);

		return LoadShader(Path,ShaderType);
	}
	else
	{
		return Find->second;
	}
}
GLuint Resource::LoadShader_Program(std::string VertexFile, std::string FragmentFile)
{
	// We have to make a name that is unique but avoids loading the same files over and over.
	// To do this, we just combine the two file names, making a unique-only-to-those-files name.

	string	Name = VertexFile+FragmentFile;
	auto	Find = Shaders.find(Name);

	if (Find == Shaders.end())
	{
		GLuint	VertexShader	= Resource::LoadShader		(VertexFile	 ,	GL_VERTEX_SHADER);
		GLuint	FragmentShader	= Resource::LoadShader		(FragmentFile,	GL_FRAGMENT_SHADER);
		GLuint	Program			= Shader::AttachToProgram	(VertexShader,	FragmentShader);

		if (Program == NULL)
		{
			Utility::Log("Resource: LoadShader_Program() Failed, Shader::AttachToProgram() returned NULL program.");
			return (GLuint) NULL;
		}
		pair<string,GLuint>		Pair(Name,Program);
		Shaders.insert(Shaders.end(),Pair);
		return Program;
	}
	else
	{
		return Find->second;
	}

}

Node Resource::LoadConfig(string FileName)
{

	return Collada::Load(Resource::AliasAdd(Path::Config,FileName));
}


// |---------- Resource::FileSystem ----------|
std::map<std::string,std::string> Resource::Keys;

void	Resource::Setup_FileSystem(char* Root)
{	
	std::string directory = Root;

	Keys[Path::Root]      = directory.substr(0,directory.find_last_of("/\\"));
	Keys[Path::Data]      = AliasAdd(Path::Root,Path::Data);
	Keys[Path::Sound]     = AliasAdd(Path::Data,Path::Sound);
	Keys[Path::Config]    = AliasAdd(Path::Data,Path::Config);
	Keys[Path::Models]    = AliasAdd(Path::Data,Path::Models);
	Keys[Path::Shaders]   = AliasAdd(Path::Data,Path::Shaders);
	Keys[Path::Materials] = AliasAdd(Path::Data,Path::Materials);

	//cout << (Paths[Root] += "Test") << endl;
}

void	Resource::AliasCreate(string Alias, string Path)
{
	Keys[Alias] = Path; // += Key(Alias,Path);
}
string	Resource::AliasGet(string Alias)
{
	return Keys[Alias];
}
string	Resource::AliasAdd(string Alias, string Path)
{
	return DirFix(AliasGet(Alias)+"/"+Path);
}

string  Resource::DirFix(string Path) 
{
	// Sometimes when merging paths, back/forward slashes will be duplicated or mixed; this function formats them.

	string Token1 = "\\"; // Escape Char 
	string Token2 = "//"; // Normal
	unsigned int Pos1, Pos2;

	do
	{
		Pos1 = Path.find(Token1);
		if (Pos1!=-1)  {Path.replace(Pos1, Token1.length(), "/");}

		Pos2 = Path.find(Token2);
		if (Pos2!=-1)  {Path.replace(Pos2, Token2.length(), "/");}
	}
	while( ((Pos1 != string::npos) || (Pos2 != string::npos) ));


	return Path;

}

bool Resource::File_Exists(string Alias,string FileName)
{
	string Path = AliasAdd(Alias,FileName);

	ifstream File;
	File.open(Path);
	if (File.is_open())
	{	
		File.close();
		return true;
	}
	else
	{
		return false;
	}
}
bool Resource::File_Exists(string Path)
{
	ifstream File;
	File.open(Path);
	if (File.is_open())
	{	
		File.close();
		return true;
	}
	else
	{
		return false;
	}
}
bool Resource::File_Load(string Path,string &Buffer )
{
	if (File_Exists(Path) == true)
	{

		ifstream File;
		File.open(Path);
		if(!File.is_open())
		{
			Utility::Log("FileSystem: Failed to read file ["+Path+"]");
			return false;
		}

		stringstream Data;
		Data << File.rdbuf();
		File.close();
		Buffer = Data.str();
		return true;
	}
	else
	{
		Utility::Log("FileSystem: File ["+Path+"] does not exist.");
		return false;
	}
}

